#include <iostream>
#include <stdlib.h>
#include <fstream>
#include "Parser.h"
#include "SystemView.h"
#include "OptimalAlgo.h"
#include "NextAlgo.h"
#include "LunTotalStatistics.h"
#include "Writer.h"
#include "CostAlgo.h"
#include "CAlgo.h"
#include "CalcOverlap.h"
#include "SystemRecommendation.h"

//#include <vld.h>

using namespace std;

vector <string> split (string str, char delimiter)
{
	vector<string> ans;
	stringstream ss (str);
	int length = str.length();
	char *a=new char[str.size()+1];
	while (ss.good() )
	{
		ss.getline(a, length, delimiter);
		if (strlen(a)==0)
			continue;
		ans.push_back(string(a));
	}
	delete [] a;
	return ans;
}

void printDamage (Algorithm* a){
	map<int, double>::iterator it;
	map<int, double>* damage = a->getDamage();
	cout << "\Damage: " << endl;
	for (it = damage->begin(); it != damage->end(); it++){
		cout << it->first << " " << it->second << endl;
	}
	cout << "" << endl;
}

void printOverlaps(CalcOverlap* overlap){
	map<int, double>::iterator mapIter;
	cout<<"\nGROUP 1 OVERLAPS - desired response time: "<<overlap->group1Desired<<endl;
	for (mapIter = overlap->group1Overlaps.begin(); mapIter!=overlap->group1Overlaps.end(); mapIter++){
		cout<<"time: "<<(*mapIter).first<<endl;
		cout<<"hit ratio: "<<(*mapIter).second<<endl;
	}
	cout<<"\nGROUP 2 OVERLAPS - desired response time: "<<overlap->group2Desired<<endl;
	for (mapIter = overlap->group2Overlaps.begin(); mapIter!=overlap->group2Overlaps.end(); mapIter++){
		cout<<"time: "<<(*mapIter).first<<endl;
		cout<<"hit ratio: "<<(*mapIter).second<<endl;
	}
	cout<<"\nGROUP 3 OVERLAPS - desired response time: "<<overlap->group3Desired<<endl;
	for (mapIter = overlap->group3Overlaps.begin(); mapIter!=overlap->group3Overlaps.end(); mapIter++){
		cout<<"time: "<<(*mapIter).first<<endl;
		cout<<"hit ratio: "<<(*mapIter).second<<endl;
	}
}

void runC(SystemView* sys, Writer* writer, Parser* parser){
	cout << "\nRun 1" << endl;
	CostAlgo* cost = new CostAlgo(sys, 0, writer, parser, 0.5, 1000, sys->getLimit());
	cost->runAlgo(1);
	writer->DeviceUtilStatistics("1");
	writer->writeGroupRT("1");
	delete cost;

	if (DEBUG>1){
		writer->writeLog("number of luns: " + sys->getMy_luns()->size());
	}

	cout << "\nRun 2" << endl;
	parser->startOver();
	sys->divide_all_lun_priorities(1);	
	cost = new CostAlgo(sys, 0, writer, parser, 0.5, 1000, sys->getLimit());
	cost->runAlgo(2);
	writer->DeviceUtilStatistics("2");
	writer->writeGroupRT("2");
	delete cost;
	
	cost = new CostAlgo(sys, 0, writer, parser, 0.5, 1000, sys->getLimit());
	cout << "\nRun 3" << endl;
	parser->startOver();
	sys->divide_all_lun_priorities(1);	
	cost->runAlgo(3);
	writer->DeviceUtilStatistics("3");
	delete cost;
	writer->writePriorityGroups();
	cout<<endl;
}

void runNext(SystemView* sys, Writer* writer, Parser* parser){
	cout << "\nRun 1" << endl;
	NextAlgo* next = new NextAlgo(sys, 0, writer, parser, 0.5, 1000);
	next->runAlgo(1);
	writer->DeviceUtilStatistics("1");
	writer->writeGroupRT("1");
	delete next;
	
	if (DEBUG>1){
		writer->writeLog("number of luns: " + sys->getMy_luns()->size());
	}

	cout << "\nRun 2" << endl;
	parser->startOver();
	sys->divide_all_lun_priorities(1);	
	next = new NextAlgo(sys, 0, writer, parser, 0.5, 1000);
	next->runAlgo(2);
	writer->DeviceUtilStatistics("2");
	writer->writeGroupRT("2");
	delete next;
	
	next = new NextAlgo(sys, 0, writer, parser, 0.5, 1000);
	cout << "\nRun 3" << endl;
	parser->startOver();
	sys->divide_all_lun_priorities(1);	
	next->runAlgo(3);
	writer->DeviceUtilStatistics("3");
	delete next;
	writer->writePriorityGroups();
	cout<<endl;
}

void runOptimal(SystemView* sys, Writer* writer, Parser* parser){
	cout << "\nRun 1" << endl;
	OptimalAlgo* optimal = new OptimalAlgo(sys, 0, writer, parser);
	optimal->runAlgo(1);
	writer->DeviceUtilStatistics("1");
	writer->writeGroupRT("1");
	delete optimal;

	cout << "\nRun 2" << endl;
	parser->startOver();
	sys->divide_all_lun_priorities(1);	
	optimal = new OptimalAlgo(sys, 0, writer,parser);
	optimal->runAlgo(2);
	writer->DeviceUtilStatistics("2");
	writer->writeGroupRT("2");
	delete optimal;

	cout << "\nRun 3" << endl;
	parser->startOver();
	optimal = new OptimalAlgo(sys, 0, writer,parser);
	optimal->runAlgo(3);
	writer->DeviceUtilStatistics("3");
	writer->writeGroupRT("3");
	delete optimal;
	writer->writePriorityGroups();
}

int main(){
	Parser* parser = new Parser("..\\DataBase\\StorageCharacteristics.cfg", "..\\DataBase\\UserInput.cfg");
	//Next_LRU_Groups_1-4-5_Devices_5_30_50_Desired_5_10_15
	SystemView* sys = parser->createSystem();

	stringstream outputFilesName;
	switch(parser->whichAlgo){
	case 1:	outputFilesName<<"Optimal_";
			break;
	case 2: outputFilesName<<"Next_";
			break;
	case 3: outputFilesName<<"Cost_";
			break;
	}
	string cache = parser->dataFileNames->at(0);
	vector<string> parts = split(cache, '_');
	outputFilesName<<parts.at(2)<<"_";
	outputFilesName<<"Groups_";
	double* group_percents = sys->getAllocation()->getGroupPercents();
	outputFilesName << group_percents[0] << "-" << group_percents[1] << "-" << group_percents[2] << "_Devices_";
	outputFilesName << parser->SSD_num << "-" << parser->FC_num << "-" << parser->SATA_num << "_Desired_";
	outputFilesName << parser->response_times->at(0) << "-" << parser->response_times->at(1) << "-" << parser->response_times->at(2) << ".txt";
	cout<<"outputFileName:"<<endl;
	cout<<outputFilesName.str()<<endl;
	//Next_LRU_Groups_1-4-5_Devices_5_30_50_Desired_5_10_15

	ofstream out;
	out.open("E:/InputFiles/ExtentPlacements/ExtentPlacements_" + outputFilesName.str());
	out.close();
	out.open("E:/InputFiles/ExtentPlacements/ExtentPlacementsNoPriority_" + outputFilesName.str());
	out.close();
	out.open("E:/InputFiles/LunsPriorityGroups/Groups_" + outputFilesName.str());
	out.close();

	Writer* writer = new Writer(sys, outputFilesName.str(), parser->response_times);
	SystemRecommendation* SR = new SystemRecommendation();
	int* nof_devices = 0;
	
	if (sys->getAllocation()->getSystem_cost() > 0){
		// system cost: 13500 -> 0<SSD<10, 8500 -> 0<SSD<5 (add 100 for each extra FC desired)
		nof_devices = SR->ChooseBest(sys->getAllocation()->getSystem_cost(), sys->getAllocation()->getFix_sata(), parser, sys, writer);		// system_cost, nof_SATA, algorithm, files
	}
	else {
		switch(parser->whichAlgo){
		case 1:	cout<<"Runing Optimal"<<endl;
				runOptimal(sys, writer, parser);
				break;
		case 2: cout<<"Runing Next"<<endl;
				runNext(sys, writer, parser);
				break;
		case 3: cout<<"Runing Cost"<<endl;
				runC(sys, writer, parser);
				break;
		default:
			cout<<"NO ALGO TO RUN!!"<<endl;
		}
	}

//	parser->startOver();
//	CalcOverlap* overlap = new CalcOverlap(parser);
//	overlap->calculate();
//	printOverlaps(overlap);

//	sys->writeAllocStats();

	if (nof_devices) {
		delete nof_devices;
	}
	
	delete SR;
	delete writer;
	delete parser;

	cout<<"The End\n";
	int i;
	cin >> i;
	return 0;
};

